using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Controls;
using Phoenix.Exceptions;
using Phoenix.Utilities;

namespace Phoenix.Framework
{
    public class DefaultViewLocator : TypeLocatorBase<Control>, IViewLocator
    {
        public DefaultViewLocator(params Assembly[] additionalAssemblies) :
            this(new AssemblyTypeProvider(), additionalAssemblies)
        {
        }

        public DefaultViewLocator(IAssemblyTypeProvider assemblyTypeProvider, params Assembly[] additionalAssemblies) :
            base(assemblyTypeProvider, additionalAssemblies)
        {
        }

        public Control Locate(ControllerAction controllerAction)
        {
            var viewName = controllerAction.Action;
            if (viewName.EndsWith("Async"))
                viewName = viewName.Substring(0, viewName.Length - 5);

            var potentialMatches = MatchingTypes
                .Where(t => NameMatches(viewName, t) && InAssembly(t, controllerAction.Assembly))
                .ToList();

            if (potentialMatches.Count == 1)
                return CreateView(potentialMatches[0]); //success

            if (potentialMatches.Count > 1)
            {
                var inNamespace = potentialMatches
                    .Where(t => InNamespaceNamed(t, controllerAction.Controller))
                    .ToList();

                if (inNamespace.Count == 1)
                    return CreateView(inNamespace[0]); // success

                if (inNamespace.Count == 0)
                    throw AmbiguousMatch(potentialMatches, controllerAction.Controller, viewName);

                if (inNamespace.Count > 1)
                    throw AmbiguousMatch(inNamespace, controllerAction.Controller, viewName);
            }

            throw ViewNotFound(controllerAction.Controller, viewName);
        }

        static bool InAssembly(Type type, string assembly)
        {
            if (string.IsNullOrEmpty(assembly))
                return true;

            return type.Assembly.GetAssemblyName() == assembly;
        }

        static bool NameMatches(string viewName, Type t)
        {
            return SuffixHelper.RemoveViewSuffixes(t.Name) == SuffixHelper.RemoveViewSuffixes(viewName);
        }

        static AmbiguousMatchException AmbiguousMatch(IEnumerable<Type> inNamespace, string controllerName, string viewName)
        {
            var messageBuilder = new StringBuilder();

            messageBuilder.AppendFormat("Ambiguous match when trying to locate view {0}", viewName);
            messageBuilder.AppendLine();
            messageBuilder.AppendLine("Potential matches: ");
            foreach (var type in inNamespace)
            {
                messageBuilder.AppendFormat(" - {0}", type.FullName);
            }

            messageBuilder.AppendLine();
            messageBuilder.AppendLine();

            messageBuilder.AppendFormat("Try putting the view in a folder which matches the controller name of {0}", controllerName);

            return new AmbiguousMatchException(messageBuilder.ToString());
        }

        ViewNotFoundException ViewNotFound(string controllerName, string viewName)
        {
            var message = string.Format(
                "Unable to find view named {0}. The view should be called {1} and be in a folder called {2}.",
                viewName, SuffixHelper.RemoveViewSuffixes(viewName), SuffixHelper.RemoveControllerSuffixes(controllerName));
            return new ViewNotFoundException(message);
        }

        bool InNamespaceNamed(Type type, string controllerName)
        {
            var ns = type.Namespace;
            if (ns == null)
                return false;


            var folder = ns.Split('.').Last();

            return SuffixHelper.RemoveControllerSuffixes(folder) == SuffixHelper.RemoveControllerSuffixes(controllerName);
        }

        static Control CreateView(Type viewType)
        {
            return (Control)Activator.CreateInstance(viewType);
        }

        protected override bool NameMatchesConvention(MemberInfo t)
        {
            return true;
        }
    }
}